package com.shinhye.elephantsafe.engine;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.SystemClock;

import com.shinhye.elephantsafe.domain.AppInfo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AppInfomations {
	private Map<String, AppInfo> map;
	private List<AppInfo> list;
    
	/**
	 * Get a list with all installed application's information.
	 * @param context
	 * @return A list with all AppInfo.
	 */
	public List<AppInfo> getAllAppInfos(Context context) {
		list = new ArrayList<AppInfo>();
		map = new HashMap<String, AppInfo>();
		
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packInfoList = pm.getInstalledPackages(0);
		for(PackageInfo packInfo : packInfoList) {
			AppInfo appInfo = new AppInfo();
			Drawable icon = packInfo.applicationInfo.loadIcon(pm);
			String name = packInfo.applicationInfo.loadLabel(pm).toString();
			String packName = packInfo.packageName;
			
			appInfo.setIcon(icon);
			appInfo.setName(name);
			appInfo.setPackName(packName);
			
			int flag = packInfo.applicationInfo.flags;
			if((flag & ApplicationInfo.FLAG_SYSTEM) == 0) {
				appInfo.setUser(true);
			} else {
				appInfo.setUser(false);
			}
			
			if((flag & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) {
				appInfo.setRom(true);
			} else {
				appInfo.setRom(false);
			}
			
			list.add(appInfo);
			map.put(packName, appInfo);
		}
		
		loadSize(pm);
		
		SystemClock.sleep(200);  //wait for remote service get information before return.
		return list;
	}
	
	/**
	 * Start to load all application's size info by invoking remote service(cost many time).
	 * @param pm
	 */
	public void loadSize(PackageManager pm) {
		for(AppInfo appInfo : list) {
			String packName = appInfo.getPackName();
	        try {
				//通过反射机制获得该隐藏函数  
				Method getPackageSizeInfo = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class);  
				//调用该函数，并且给其分配参数 ，待调用流程完成后会回调PkgSizeObserver类的函数  
				getPackageSizeInfo.invoke(pm, packName, new PkgSizeObserver());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	//AIDL文件形成的Binder机制服务类  
    private class PkgSizeObserver extends IPackageStatsObserver.Stub{  

		/** 
		 * Call back operation in a sub-thread. After remote get-states operation complete, it will do the callback.
         * @param pStats Get size data in this object.
         * @param succeeded If call back success return true. 
         */   
        @Override  
        public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                throws RemoteException {
        	AppInfo info = map.get(pStats.packageName);
        	info.setCachesize(pStats.cacheSize);
        	info.setCodesize(pStats.codeSize);
        	info.setDatasize(pStats.dataSize);
        	info.setAppsize(pStats.cacheSize + pStats.codeSize + pStats.dataSize);
        }  
    }
	
	/**
	 * Get a list with all launch-able application's simple information. Be attention that this method can only get a AppInfo Bean with icon, name and packName attribute.
	 * @param context
	 * @return A list with launch-able AppInfo.
	 */
	public static List<AppInfo> getLaunchableAppInfos(Context context) {
		List<AppInfo> slist = new ArrayList<AppInfo>();
		
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packInfoList = pm.getInstalledPackages(0);
		for(PackageInfo packInfo : packInfoList) {
			if(pm.getLaunchIntentForPackage(packInfo.packageName) != null) {
				AppInfo appInfo = new AppInfo();
				Drawable icon = packInfo.applicationInfo.loadIcon(pm);
				String name = packInfo.applicationInfo.loadLabel(pm).toString();
				String packName = packInfo.packageName;
				
				appInfo.setIcon(icon);
				appInfo.setName(name);
				appInfo.setPackName(packName);
				
				slist.add(appInfo);
			}
		}
		
		return slist;
	}
    
}
